ποΈGitΠ―ΡΠ°ποΈ
specs/006-kmp-project-structure/quickstart.md 936be3998f0fe49f58b8a0f9ff2c01e3cbd85609 (936be399) Text, 5.94 KB
Quickstart: KMP Recommended Project Structure Migration
Prerequisites
β’ JDK 21 installed
β’ T383838ANDROID_HOME set
β’ Proto submodule initialized: T383838git submodule update --init
β’ T383838local.properties exists: T383838[ -f local.properties ] || cp secrets.defaults.properties local.properties
β’ Clean build baseline: T383838./gradlew assembleDebug passes before starting
Migration Steps
Step 1: Harden convention plugin defaults
File: T383838build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt
In T383838configureKotlinMultiplatform(), add T383838androidResources.enable = false as default inside the T383838pluginManager.withPlugin block:
T282828
Te6edf3pluginManagerTb4b4b4.Te6edf3withPluginTb4b4b4(Ta5d6ff"Ta5d6ffcom.android.kotlin.multiplatform.libraryTa5d6ff"Tb4b4b4) Tb4b4b4{
Te6edf3extensionsTb4b4b4.Te6edf3findByTypeTff7b72<Te6edf3KotlinMultiplatformAndroidLibraryTargetTff7b72>Tb4b4b4(Tb4b4b4)Tff7b72?.Te6edf3apply Tb4b4b4{
Te6edf3compileSdk Tff7b72= Te6edf3configPropertiesTb4b4b4.Te6edf3getPropertyTb4b4b4(Ta5d6ff"Ta5d6ffCOMPILE_SDKTa5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)
Te6edf3minSdk Tff7b72= Te6edf3configPropertiesTb4b4b4.Te6edf3getPropertyTb4b4b4(Ta5d6ff"Ta5d6ffMIN_SDKTa5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)
Te6edf3androidResourcesTb4b4b4.Te6edf3enable Tff7b72= Tff7b72false T8b949e// β NEW: Default for all KMP modules
Tff7b72if Tb4b4b4(Te6edf3namespace Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3pkg Tff7b72= Tff7b72thisTf0883e@configureKotlinMultiplatform.path.removePrefixTb4b4b4(Ta5d6ff"Ta5d6ff:Ta5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3replaceTb4b4b4(Ta5d6ff"Ta5d6ff:Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff.Ta5d6ff"Tb4b4b4)
Te6edf3namespace Tff7b72= Ta5d6ff"Ta5d6fforg.meshtastic.Tffd700$Te6edf3pkgTa5d6ff"
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Verify: T383838./gradlew assembleDebug β should pass (no behavior change yet, modules still override).
Step 2: Migrate Tier 1 modules (minimal β 6 modules)
For each module, change T383838android {} to T383838androidLibrary {} inside T383838kotlin {}, removing properties now handled by convention.
Pattern β before:
T282828
Te6edf3kotlin Tb4b4b4{
Te6edf3jvmTb4b4b4(Tb4b4b4) T8b949e// β remove if present (convention handles it)
Te6edf3android Tb4b4b4{
Te6edf3namespace Tff7b72= Ta5d6ff"Ta5d6fforg.meshtastic.core.diTa5d6ff" T8b949e// β auto-derived, remove
Te6edf3androidResourcesTb4b4b4.Te6edf3enable Tff7b72= Tff7b72false T8b949e// β convention default, remove
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Pattern β after:
T282828
Te6edf3kotlin Tb4b4b4{
T8b949e// android target configured by convention plugin (namespace auto-derived, resources disabled)
Te6edf3sourceSets Tb4b4b4{ T8b949e/* ... */ Tb4b4b4}
Tb4b4b4}
Modules: T383838core:di, T383838core:nfc, T383838core:ui, T383838core:navigation, T383838feature:messaging, T383838feature:settings
Verify after each module: T383838./gradlew :<module>:assembleDebug :<module>:allTests
Verify after batch: T383838./gradlew assembleDebug allTests
Step 3: Migrate Tier 2 modules (withHostTest β 18 modules)
Pattern β before:
T282828
Te6edf3kotlin Tb4b4b4{
Te6edf3jvmTb4b4b4(Tb4b4b4)
Te6edf3android Tb4b4b4{
Te6edf3namespace Tff7b72= Ta5d6ff"Ta5d6fforg.meshtastic.core.dataTa5d6ff"
Te6edf3androidResourcesTb4b4b4.Te6edf3enable Tff7b72= Tff7b72false
Te6edf3withHostTest Tb4b4b4{ Te6edf3isIncludeAndroidResources Tff7b72= Tff7b72true Tb4b4b4}
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Pattern β after:
T282828
Te6edf3kotlin Tb4b4b4{
Te6edf3androidLibrary Tb4b4b4{
Te6edf3withHostTest Tb4b4b4{ Te6edf3isIncludeAndroidResources Tff7b72= Tff7b72true Tb4b4b4}
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Special case β T383838feature:wifi-provision (namespace mismatch):
T282828
Te6edf3kotlin Tb4b4b4{
Te6edf3androidLibrary Tb4b4b4{
Te6edf3namespace Tff7b72= Ta5d6ff"Ta5d6fforg.meshtastic.feature.wifiprovisionTa5d6ff"
Te6edf3withHostTest Tb4b4b4{Tb4b4b4}
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Verify after batch: T383838./gradlew assembleDebug :desktop:packageUberJarForCurrentOS allTests
Step 4: Migrate Tier 3 modules (special β 3 modules)
T383838core:proto:
T282828
Te6edf3kotlin Tb4b4b4{
Te6edf3androidLibrary Tb4b4b4{
Te6edf3minSdk Tff7b72= T79c0ff2T79c0ff1 T8b949e// ATAK compatibility override
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
T383838core:database:
T282828
Te6edf3kotlin Tb4b4b4{
Te6edf3androidLibrary Tb4b4b4{
Te6edf3withHostTest Tb4b4b4{ Te6edf3isIncludeAndroidResources Tff7b72= Tff7b72true Tb4b4b4}
Te6edf3withDeviceTest Tb4b4b4{ Te6edf3instrumentationRunner Tff7b72= Ta5d6ff"Ta5d6ffandroidx.test.runner.AndroidJUnitRunnerTa5d6ff" Tb4b4b4}
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
T383838core:resources:
T282828
Te6edf3kotlin Tb4b4b4{
Te6edf3androidLibrary Tb4b4b4{
Te6edf3androidResources Tb4b4b4{
Te6edf3enable Tff7b72= Tff7b72true T8b949e// Override convention default
Te6edf3resourcePrefix Tff7b72= Ta5d6ff"Ta5d6ffmeshtastic_Ta5d6ff"
Tb4b4b4}
Te6edf3withHostTest Tb4b4b4{ Te6edf3isIncludeAndroidResources Tff7b72= Tff7b72true Tb4b4b4}
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Verify: T383838./gradlew assembleDebug :desktop:packageUberJarForCurrentOS allTests
Step 5: Full verification
T282828
T8b949e# Full Android build
./gradlew assembleDebug
T8b949e# Full Desktop build
./gradlew :desktop:packageUberJarForCurrentOS
T8b949e# All tests
./gradlew allTests
T8b949e# Desktop-only mode (no Android SDK)
Te6edf3DESKTOP_ONLYTff7b72=Tffa657true ./gradlew :desktop:packageUberJarForCurrentOS
T8b949e# Lint and formatting
./gradlew spotlessApply spotlessCheck detekt
T8b949e# Verify no legacy android {} blocks remain in KMP modules
grep -rn Ta5d6ff"android {" core/*/build.gradle.kts feature/*/build.gradle.kts Tb4b4b4| grep -v Ta5d6ff"widget\|api\|barcode\|androidLibrary\|androidResources\|androidMain\|androidHostTest\|androidDeviceTest\|androidRuntimeClasspath"
T8b949e# Expected: zero matches
Step 6: Validate success criteria
β’ [ ] SC-001: Zero T383838android {} blocks inside T383838kotlin {} in KMP modules
β’ [ ] SC-002: T383838assembleDebug and T383838:desktop:packageUberJarForCurrentOS pass
β’ [ ] SC-003: T383838allTests passes with zero regressions
β’ [ ] SC-004: T383838DESKTOP_ONLY build succeeds
β’ [ ] SC-005: Clean T383838assembleDebug time within 5% of baseline
β’ [ ] SC-006: New module can apply T383838meshtastic.kmp.library with no manual T383838android {} block
β’ [ ] SC-007: T383838configureKotlinMultiplatform() uses only T383838KotlinMultiplatformAndroidLibraryTarget API
Rollback Plan
If any step produces build failures that cannot be resolved:
Per-module: T383838git checkout -- <module>/build.gradle.kts to revert a single module migration.
Per-tier:
β’ Tier 1: T383838git checkout -- core/di/build.gradle.kts core/nfc/build.gradle.kts core/ui/build.gradle.kts core/navigation/build.gradle.kts feature/settings/build.gradle.kts feature/messaging/build.gradle.kts
β’ Tier 2: T383838git checkout -- core/{ble,common,data,domain,model,network,service,takserver,datastore,prefs,repository,testing}/build.gradle.kts feature/{connections,firmware,intro,map,node,wifi-provision}/build.gradle.kts
β’ Tier 3: T383838git checkout -- core/{proto,database,resources}/build.gradle.kts
Full rollback: T383838git stash or T383838git checkout -- . to revert all changes.
Convention plugin change (Step 1) is backward-compatible β existing explicit T383838androidResources.enable = false in modules is a no-op override.
Served by rngit 1.5.0 - Generated in 0.06s